package com.gserver.game.plugin.impl;

import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.gserver.commons.script.ScriptManager;
import com.gserver.game.plugin.IPlugin;
import com.gserver.game.plugin.IPluginManager;
import com.gserver.game.plugin.IPointPlugin;
import com.gserver.game.plugin.PluginConfig;
import com.gserver.game.plugin.PluginPoint;

/**
 * 插件管理器
 * 
 * 
 * 
 */
public class PluginManagerImpl<E extends Enum<E> & PluginPoint<E>> implements
		IPluginManager<E> {

	private static final Logger logger = LoggerFactory
			.getLogger(PluginManagerImpl.class);
	/** 插件列表 */
	private final List<IPlugin> plugins = new ArrayList<IPlugin>();
	/** 支持扩展点的插件列表 */
	private final Map<E, List<IPointPlugin<E, Object>>> pointPlugins;

	/**
	 * 加载配置的Plugin
	 * 
	 * @param pluginConfig
	 * 
	 */
	@SuppressWarnings("unchecked")
	public PluginManagerImpl(String pluginCfg, ScriptManager scriptEngine) {
		if (pluginCfg == null) {
			this.pointPlugins = null;
			return;
		}
		List<PluginConfig> _pluginConfigs = loadPluginConfigs(pluginCfg,
				scriptEngine);

		Map<E, List<IPointPlugin<E, Object>>> _map = new HashMap<E, List<IPointPlugin<E, Object>>>();
		for (PluginConfig _pluginConfig : _pluginConfigs) {
			try {
				Class _class = Class
						.forName(_pluginConfig.getPluginClassName());
				if (IPlugin.class.isAssignableFrom(_class)) {
					IPlugin _plugin = null;
					if (_pluginConfig.getConfig() != null) {
						Constructor _constructor = _class
								.getConstructor(Object.class);
						_plugin = (IPlugin) _constructor
								.newInstance(_pluginConfig.getConfig());
					} else {
						_plugin = (IPlugin) _class.newInstance();
					}
					this.plugins.add(_plugin);
					if (IPointPlugin.class.isAssignableFrom(_class)) {
						IPointPlugin _pointPlugin = (IPointPlugin) _plugin;
						EnumSet<E> _set = _pointPlugin.getPluginPoint();
						if (_set != null) {
							for (E _enum : _set) {
								List<IPointPlugin<E, Object>> _pointPluginList = _map
										.get(_enum);
								if (_pointPluginList == null) {
									_pointPluginList = new ArrayList<IPointPlugin<E, Object>>();
									_map.put(_enum, _pointPluginList);
								}
								_pointPluginList.add(_pointPlugin);
							}
						}
					}
				} else {
					if (logger.isWarnEnabled()) {
						logger.warn("The plugin class:" + _pluginConfig
								+ " is not a implements of " + IPlugin.class);
					}
				}
			} catch (Exception e) {
				if (logger.isErrorEnabled()) {
					logger.error("Load plugin " + _pluginConfig + " error.", e);
				}
			}
		}
		this.pointPlugins = _map;

	}

	public void start() {
		for (IPlugin _plugin : this.plugins) {
			_plugin.start();
		}
	}

	public void stop() {
		for (IPlugin _plugin : this.plugins) {
			_plugin.stop();
		}
	}

	public void fire(E point, Object object) {
		if (this.pointPlugins == null) {
			return;
		}
		List<IPointPlugin<E, Object>> _pointPlugins = this.pointPlugins
				.get(point);
		if (_pointPlugins != null) {
			for (IPointPlugin<E, Object> _plugin : _pointPlugins) {
				_plugin.fire(point, object);
			}
		}
	}

	public void check() {
		for (IPlugin _plugin : this.plugins) {
			_plugin.check();
		}
	}

	public void init() {
		for (IPlugin _plugin : this.plugins) {
			_plugin.init();
		}
	}

	public List<IPlugin> getPlugins() {
		return plugins;
	}

	/**
	 * 从指定的配置文件中加载插件的配置
	 * 
	 * @param pluginCfg
	 * @param scriptEngine
	 * @return
	 */
	private List<PluginConfig> loadPluginConfigs(String pluginCfg,
			ScriptManager scriptManager) {
		Map<String, Object> _bindings = new HashMap<String, Object>();
		List<String> _pluginConfigs = new ArrayList<String>();
		// 依次加载各配置文件
		_bindings.put("config", _pluginConfigs);
		if (logger.isInfoEnabled()) {
			logger.info("Load plugin configs from file :" + pluginCfg);
		}
		// 加载所有的插件配置
		scriptManager.runScript(_bindings,
				scriptManager.getScriptContent(pluginCfg));

		final List<PluginConfig> _pluginConfigObj = new ArrayList<PluginConfig>();

		_bindings.put("config", new PluginObject(_pluginConfigObj));

		for (String _pluginConfig : _pluginConfigs) {
			if (logger.isInfoEnabled()) {
				logger.info("Load Plugin from file :" + _pluginConfig);
			}
			scriptManager.runScript(_bindings,
					scriptManager.getScriptContent(_pluginConfig));
		}
		return _pluginConfigObj;
	}

	public class PluginObject {

		final List<PluginConfig> _pluginConfigObj;

		public PluginObject(List<PluginConfig> _pluginConfigObj) {
			this._pluginConfigObj = _pluginConfigObj;
		}

		public void add(String pluginClassName, Object config) {
			_pluginConfigObj.add(new PluginConfig(pluginClassName, config));
		}
	}

}
